<!DOCTYPE html>
<!--
Copyright (c) 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/base.html">
<link rel="import" href="/tracing/base/unit.html">
<link rel="import" href="/tracing/base/utils.html">
<link rel="import" href="/tracing/model/event_set.html">
<link rel="import" href="/tracing/ui/analysis/analysis_sub_view.html">
<link rel="import" href="/tracing/ui/analysis/generic_object_view.html">
<link rel="import" href="/tracing/ui/analysis/stack_frame.html">
<link rel="import" href="/tracing/ui/base/table.html">
<link rel="import" href="/tracing/ui/base/ui.html">
<link rel="import" href="/tracing/value/ui/scalar_span.html">

<dom-module id='tr-ui-a-single-event-sub-view'>
  <template>
    <style>
    :host {
      display: flex;
      flex: 0 1;
      flex-direction: column;
    }
    #table {
      flex: 0 1 auto;
      align-self: stretch;
      font-size: 12px;
    }
    </style>
    <tr-ui-b-table id="table">
    </tr-ui-b-table>
  </template>
</dom-module>
<script>
'use strict';

Polymer({
  is: 'tr-ui-a-single-event-sub-view',
  behaviors: [tr.ui.analysis.AnalysisSubView],

  properties: {
    isFlow: {
      type: Boolean,
      value: false
    }
  },

  ready() {
    this.currentSelection_ = undefined;
    this.$.table.tableColumns = [
      {
        title: 'Label',
        value(row) { return row.name; },
        width: '150px'
      },
      {
        title: 'Value',
        width: '100%',
        value(row) { return row.value; }
      }
    ];
    this.$.table.showHeader = false;
  },

  get selection() {
    return this.currentSelection_;
  },

  set selection(selection) {
    if (selection.length !== 1) {
      throw new Error('Only supports single slices');
    }
    this.setSelectionWithoutErrorChecks(selection);
  },

  setSelectionWithoutErrorChecks(selection) {
    this.currentSelection_ = selection;
    this.updateContents_();
  },

  getFlowEventRows_(event) {
    // TODO(nduca): Figure out if there is a cleaner way to do this.

    const rows = this.getEventRowsHelper_(event);

    // Put the ID up top.
    rows.splice(0, 0, {
      name: 'ID',
      value: event.id
    });

    function createLinkTo(slice) {
      const linkEl = document.createElement('tr-ui-a-analysis-link');
      linkEl.setSelectionAndContent(function() {
        return new tr.model.EventSet(slice);
      });
      Polymer.dom(linkEl).textContent = slice.userFriendlyName;
      return linkEl;
    }

    rows.push({
      name: 'From',
      value: createLinkTo(event.startSlice)
    });
    rows.push({
      name: 'To',
      value: createLinkTo(event.endSlice)
    });
    return rows;
  },

  getEventRowsHelper_(event) {
    const rows = [];

    if (event.error) {
      rows.push({ name: 'Error', value: event.error });
    }

    if (event.title) {
      let title = event.title;
      if (tr.isExported('tr-ui-e-chrome-codesearch')) {
        const container = document.createElement('div');
        container.appendChild(document.createTextNode(title));
        const link = document.createElement('tr-ui-e-chrome-codesearch');
        link.searchPhrase = title;
        container.appendChild(link);
        title = container;
      }
      rows.push({ name: 'Title', value: title });
    }

    if (event.category) {
      rows.push({ name: 'Category', value: event.category });
    }

    if (event.model !== undefined) {
      const ufc = event.model.getUserFriendlyCategoryFromEvent(event);
      if (ufc !== undefined) {
        rows.push({ name: 'User Friendly Category', value: ufc });
      }
    }

    if (event.name) {
      rows.push({ name: 'Name', value: event.name });
    }

    rows.push({
      name: 'Start',
      value: tr.v.ui.createScalarSpan(event.start, {
        unit: tr.b.Unit.byName.timeStampInMs
      })
    });

    if (event.category === 'android' && event.model !== undefined) {
      rows.push({
        name: 'Start (Absolute time)',
        value: tr.v.ui.createScalarSpan(
            event.model.convertTimestampFromModelTime('traceEventClock',
                event.start), {
              unit: tr.b.Unit.byName.timeStampInMs
            })
      });
    }

    if (event.duration) {
      rows.push({
        name: 'Wall Duration',
        value: tr.v.ui.createScalarSpan(event.duration, {
          unit: tr.b.Unit.byName.timeDurationInMs
        })
      });
    }

    if (event.cpuDuration) {
      rows.push({
        name: 'CPU Duration',
        value: tr.v.ui.createScalarSpan(event.cpuDuration, {
          unit: tr.b.Unit.byName.timeDurationInMs
        })
      });
    }

    if (event.subSlices !== undefined && event.subSlices.length !== 0) {
      if (event.selfTime) {
        rows.push({
          name: 'Self Time',
          value: tr.v.ui.createScalarSpan(event.selfTime, {
            unit: tr.b.Unit.byName.timeDurationInMs
          })
        });
      }

      if (event.cpuSelfTime) {
        const cpuSelfTimeEl = tr.v.ui.createScalarSpan(event.cpuSelfTime, {
          unit: tr.b.Unit.byName.timeDurationInMs
        });
        if (event.cpuSelfTime > event.selfTime) {
          cpuSelfTimeEl.warning =
              ' Note that CPU Self Time is larger than Self Time. ' +
              'This is a known limitation of this system, which occurs ' +
              'due to several subslices, rounding issues, and imprecise ' +
              'time at which we get cpu- and real-time.';
        }
        rows.push({ name: 'CPU Self Time', value: cpuSelfTimeEl });
      }
    }

    if (event.durationInUserTime) {
      rows.push({
        name: 'Duration (U)',
        value: tr.v.ui.createScalarSpan(event.durationInUserTime, {
          unit: tr.b.Unit.byName.timeDurationInMs
        })
      });
    }

    function createStackFrameEl(sf) {
      const sfEl = document.createElement('tr-ui-a-stack-frame');
      sfEl.stackFrame = sf;
      return sfEl;
    }
    if (event.startStackFrame && event.endStackFrame) {
      if (event.startStackFrame === event.endStackFrame) {
        rows.push({name: 'Start+End Stack Trace',
          value: createStackFrameEl(event.startStackFrame)});
      } else {
        rows.push({ name: 'Start Stack Trace',
          value: createStackFrameEl(event.startStackFrame)});
        rows.push({ name: 'End Stack Trace',
          value: createStackFrameEl(event.endStackFrame)});
      }
    } else if (event.startStackFrame) {
      rows.push({ name: 'Start Stack Trace',
        value: createStackFrameEl(event.startStackFrame)});
    } else if (event.endStackFrame) {
      rows.push({ name: 'End Stack Trace',
        value: createStackFrameEl(event.endStackFrame)});
    }

    if (event.info) {
      const descriptionEl = tr.ui.b.createDiv({
        textContent: event.info.description,
        maxWidth: '300px'
      });
      rows.push({
        name: 'Description',
        value: descriptionEl
      });


      if (event.info.docLinks) {
        event.info.docLinks.forEach(function(linkObject) {
          const linkEl = document.createElement('a');
          linkEl.target = '_blank';
          linkEl.href = linkObject.href;
          Polymer.dom(linkEl).textContent = Polymer.dom(linkObject).textContent;
          rows.push({
            name: linkObject.label,
            value: linkEl
          });
        });
      }
    }

    if (event.associatedAlerts.length) {
      const alertSubRows = [];
      event.associatedAlerts.forEach(function(alert) {
        const linkEl = document.createElement('tr-ui-a-analysis-link');
        linkEl.setSelectionAndContent(function() {
          return new tr.model.EventSet(alert);
        }, alert.info.description);
        alertSubRows.push({
          name: alert.title,
          value: linkEl
        });
      });

      rows.push({
        name: 'Alerts', value: '',
        isExpanded: true, subRows: alertSubRows
      });
    }
    return rows;
  },

  getEventRows_(event) {
    if (this.isFlow) {
      return this.getFlowEventRows_(event);
    }

    return this.getEventRowsHelper_(event);
  },

  addArgsToRows_(rows, args) {
    let n = 0;
    for (const argName in args) {
      n += 1;
    }
    if (n > 0) {
      const subRows = [];
      for (const argName in args) {
        n += 1;
      }
      if (n > 0) {
        const subRows = [];
        for (const argName in args) {
          const argView =
              document.createElement('tr-ui-a-generic-object-view');
          argView.object = args[argName];
          subRows.push({name: argName, value: argView});
        }
        rows.push({
          name: 'Args',
          value: '',
          isExpanded: true,
          subRows
        });
      }
    }
  },

  addContextsToRows_(rows, contexts) {
    if (contexts.length) {
      const subRows = contexts.map(function(context) {
        const contextView =
            document.createElement('tr-ui-a-generic-object-view');
        contextView.object = context;
        return {name: 'Context', value: contextView};
      });
      rows.push({
        name: 'Contexts',
        value: '',
        isExpanded: true,
        subRows
      });
    }
  },

  updateContents_() {
    if (this.currentSelection_ === undefined) {
      this.$.table.rows = [];
      this.$.table.rebuild();
      return;
    }

    const event = tr.b.getOnlyElement(this.currentSelection_);

    const rows = this.getEventRows_(event);
    if (event.argsStripped) {
      rows.push({ name: 'Args', value: 'Stripped' });
    } else {
      this.addArgsToRows_(rows, event.args);
    }
    this.addContextsToRows_(rows, event.contexts);

    const customizeRowsEvent = new tr.b.Event('customize-rows');
    customizeRowsEvent.rows = rows;
    this.dispatchEvent(customizeRowsEvent);

    this.$.table.tableRows = rows;
    this.$.table.rebuild();
  }
});
</script>
